﻿#region License, Terms and Conditions
//
// Jayrock - JSON and JSON-RPC for Microsoft .NET Framework and Mono
// Written by Atif Aziz (www.raboof.com)
// Copyright (c) 2005 Atif Aziz. All rights reserved.
//
// This library is free software; you can redistribute it and/or modify it under
// the terms of the GNU Lesser General Public License as published by the Free
// Software Foundation; either version 3 of the License, or (at your option)
// any later version.
//
// This library is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
// details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with this library; if not, write to the Free Software Foundation, Inc.,
// 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 
//
#endregion

namespace Jayrock.Json.Conversion.Generation
{
    #if !NET_1_0 && !NET_1_1 && !NET_2_0

    #region Imports

    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Linq.Expressions;
    using System.Reflection;

    #endregion

    public sealed class JsonObjectImportAutomataBuilder<TContainer> :
        IEnumerable<KeyValuePair<string, JsonMemberImportAutomaton<TContainer>>>
    {
        readonly KeyValuePair<string, JsonMemberImportAutomaton<TContainer>> _importer;
        readonly JsonObjectImportAutomataBuilder<TContainer> _next;

        JsonObjectImportAutomataBuilder(string name, JsonMemberImportAutomaton<TContainer> automaton, JsonObjectImportAutomataBuilder<TContainer> next)
            : this(new KeyValuePair<string, JsonMemberImportAutomaton<TContainer>>(name, automaton), next) { }

        JsonObjectImportAutomataBuilder(KeyValuePair<string, JsonMemberImportAutomaton<TContainer>> importer, JsonObjectImportAutomataBuilder<TContainer> next)
        {
            _importer = importer;
            _next = next;
        }

        public IEnumerator<KeyValuePair<string, JsonMemberImportAutomaton<TContainer>>> GetEnumerator()
        {
            for (var node = this; node != null; node = node._next)
                yield return node._importer;
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        JsonObjectImportAutomataBuilder<TContainer> Return(string name, JsonMemberImportAutomaton<TContainer> automaton)
        {
            return new JsonObjectImportAutomataBuilder<TContainer>(name, automaton, this);
        }

        public static JsonObjectImportAutomataBuilder<TContainer> Ignore()
        {
            return new JsonObjectImportAutomataBuilder<TContainer>(null, (args, container) =>
            {
                args.Input.ReadMember();
                args.Input.Skip();
                return args.Succeeding(container);
            }, null);
        }

        public JsonObjectImportAutomataBuilder<TContainer> Member<T>(JsonImportAutomaton<T> automaton, Action<TContainer, string, T> setter)
        {
            return Return(null, (args, container) =>
            {
                var name = args.Input.ReadMember();
                var value = automaton(args);
                if (!args.Success)
                    return args.Failing<TContainer>();
                setter(container, name, value);
                return args.Succeeding(container);
            });
        }

        public JsonObjectImportAutomataBuilder<TContainer> Member<T>(Expression<Func<TContainer, T>> expression, JsonImportAutomaton<T> automaton)
        {
            return Member(null, expression, automaton);
        }

        public JsonObjectImportAutomataBuilder<TContainer> Member<T>(string name, Expression<Func<TContainer, T>> expression, JsonImportAutomaton<T> automaton)
        {
            var member = ((MemberExpression)expression.Body).Member;
            return Member(name, member, automaton);
        }

        public JsonObjectImportAutomataBuilder<TContainer> Member<T>(MemberInfo member, JsonImportAutomaton<T> automaton)
        {
            return Member(member.Name, member, automaton);
        }

        public JsonObjectImportAutomataBuilder<TContainer> Member<T>(string name, MemberInfo member, JsonImportAutomaton<T> automaton)
        {
            var obj = Expression.Parameter(typeof(TContainer));
            var val = Expression.Parameter(typeof(T));
            var body = Expression.Assign(Expression.MakeMemberAccess(obj, member), val);
            var lambda = Expression.Lambda<Action<TContainer, T>>(body, obj, val);
            var setter = lambda.Compile();
            return Member(name ?? member.Name, automaton, setter);
        }

        public JsonObjectImportAutomataBuilder<TContainer> Member<T>(string name, JsonImportAutomaton<T> automaton, Action<TContainer, T> setter)
        {
            return Return(name, (args, container) =>
            {
                var value = automaton(args);
                if (!args.Success)
                    return args.Failing<TContainer>();
                setter(container, value);
                return args.Succeeding(container);
            });
        }
    }
    
    #endif // !NET_1_0 && !NET_1_1 && !NET_2_0
}